'''
定义函数时设置的参数称为形参,调用函数时输入的参数称为实参.
函数的参数传递是指将实际参数传递给形式参数的过程
函数参数的传递可以分为 位置参数的传递,关键字参数的传递,默认参数的传递,参数的打包与解包及混合传递
'''

'''6.3.1位置参数的传递'''
# 函数在被调用时会将实参按照相应的位置依次传递给形参,即将第1个实参传递给第1个形参,将第2个实参传递给第2个形参,以此类推
def get_max(a,b):
    if a > b:
        print(f'{a}是较大的值')
    else:
        print(f'{b}是较大的值')
get_max(1,2)

'''6.3.2关键字参数的传递'''
# 关键字参数是按照 形参=实参 的格式将实参与形参相关联,将实参按照相应的关键字传递给形参
def connect(ip,port):
    print(f'设备{ip}:{port}连接')
connect(ip = '127.0.0.1',port = 8080) # 关键字传参数

'''6.3.3默认值参数的传递'''
# 函数在定义时可以指定形参的默认值,故在被调用时可以选择是否给带有默认值的形参传值,若没有给带有默认值的形参传值,则直接使用该形参的默认值
def connect(ip,port=8080):
    print(f'设备{ip}:{port}连接')
connect(ip = '127.0.0.1',port = 1000) # 传了port参数,使用传入的实参
connect(ip = '127.0.0.1') # 没有传port参数,使用默认的参数8080

'''6.3.4参数的打包与解包'''
# 打包
# 如果函数在定义时无法确定需要接收多少个数据,那么可以在定义函数时为形参添加'*'或者'**':用来接收以元组形式打包的多个值,和以字典形式打包的多个值
def test(*args):
    print(args)
# 调用test()函数时传入多个实参,多个实参会在打包后被传递给形参
test(11,22,33,44,55)
# Python解释器将传给test()函数的所有值打包成元组后传递给了形参*args

def test(**kwargs):
    print(kwargs)
# 调用test()函数时传入多个绑定关键字的实参
test(a=11,b=22,c=33,d=44,e=55)
# Python解释器将传给test()函数的多有具有关键字的实参打包成字典后传递给了形参**kwargs
# 一般建议使用*args和**kwargs,若函数没有接受发哦任何数据,参数*args和**kwargs为空,即它们为空元组或者空字典

# 解包
# 如果函数在调用时接收的实参是元组类型的数据,那么可以使用'*'将元组拆分成多个值,并将每个值按照位置参数传递的方式赋值给形参
# 如果函数在调用时接收的实参是字典类型的数据,那么可以使用'**'将字典拆分成多个键值对名将每个值按照关键字参数传递的方式赋值给与键名对应的形参
def test(a,b,c,d):
    print(a,b,c,d)

num = (1,2,3,4)
test(*(1,2,3,4))
test(*num)
num1 = {'a':11,'b':22,'c':33,'d':44}
test(**num1)
test(**{'a':11,'b':22,'c':33,'d':44})

'''6.3.5混合传递'''
# 在定义函数时,带有默认值的参数必须位于普通参数(不代默认值或标识的参数)之后,
# 带有'*'表示的参数必须位于带有默认值的参数之后,
# 带有'**'表示的参数必须位于带有'*'标识的参数之后
def test(a,b,c=33,*args,**kwargs):
    print(a,b,c,args,kwargs)
print('执行结果:')
test(1,2)
test(1,2,3)
test(1,2,3,4,5,6)
test(1,2,3,4,5,6,i=11,j=12,k=13)

'''6.4函数的返回值'''
# 函数中的return语句会在函数结束时将数据返回给程序,同时让程序回到函数被调用的位置继续执行
def filter_sensitive_words(words):
    if '山寨' in words:
        new_words = words.replace('山寨','**')
        return new_words
result = filter_sensitive_words('你的手机是山寨机')
print(result)
print(filter_sensitive_words('你的手机是山寨机'))

# 如果函数使用return语句返回了多个值,那么这些值将被保存到元组中
def move(x,y,step):
    nx = x + step
    ny = y + step
    return nx,ny # 使用return返回多个值
result = move(100,200,20) # 返回值将保存到一个元组中
print(result)  # 打印结果为一个元组

'''6.5变量作用域'''
# 变量并非在程序的任意位置都可以被访问,其访问权限取决于变量定义的位置.变量的有效范围称为该变量的作用域
'''6.5.1局部变量'''
# 局部变量指在函数内部定义的变量,只能在函数内部使用,函数执行结束之后局部变量被释放,此时无法进行访问
# 函数外部无法访问局部变量
# 不同函数内部可以包含同名的局部变量,它们相互独立,互不影响
'''6.5.1全局变量'''
# 全局变量可以在整个程序的范围内起作用,它不会受到函数范围的影像.
# 全局变量可以在程序执行的任意位置被访问,(函数内部可以访问全局变量)
# 全局变量在函数内部只能被访问,不能直接修改
'''LEGB原则'''
# L(Local):局部作用域,局部变量和形参生效的区域
# E(Enclosing):嵌套作用域,嵌套定义的函数中外层函数声明的变量生效的区域
# G(Global):全局作用域,全局变量生效的区域
# B(Built-in)内置作用域:内置模块声明的变量生效的区域
# Python在搜索变量时会按照 LEGB 这个顺序依次在这4种区域中搜索变量,若搜索到变量则终止搜索,使用搜索到的变量.若搜索完LEGB这4个区域仍无法找到变量,程序将抛出异常
'''6.5.2 global和nonlocal关键字'''
# global 关键字可以将局部变量声明为全局变量
# 语法: global 变量
number = 10 # 定义全局变量
def test_one():
    global number #使用global声明变量为全局变量
    number += 1
    print(number)
test_one()
print(number)
# nonlocal关键字可以在局部作用域中修改嵌套作用域中声明的变量
# 语法: nonlocal 变量
def test():
    number = 10
    def test_in():
        nonlocal number
        number = 20
    test_in()
    print(number)
test()